/**
 * Generated at 2012-4-26.
 * FTPToolkit.java
 */
package u.can.i.up.gearmanimpl.common;

import it.sauronsoftware.ftp4j.FTPClient;
import it.sauronsoftware.ftp4j.FTPException;
import it.sauronsoftware.ftp4j.FTPFile;
import it.sauronsoftware.ftp4j.FTPIllegalReplyException;

import java.io.File;
import java.io.IOException;
import java.util.List;

import org.apache.commons.lang.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

/**
 * @author lczgywzyy
 * @version 2013.5.28
 * 
 */
public class FTPToolkit {

	private final static Logger LOGGER = LogManager.getLogger(FTPToolkit.class);

	private FTPToolkit() {
	}

	public enum FTPOptType {
		UP("上传"), DOWN("下载"), LIST("浏览"), DELFILE("删除文件"), DELFOD("删除文件夹"), RENAME("更名");

		private String optname;

		FTPOptType(String optname) {
			this.optname = optname;
		}

		public String getOptname()
		{
			return optname;
		}
	}

	/**
	 * 创建FTP连接
	 * 
	 * @param host
	 *            主机名或IP
	 * @param port
	 *            ftp端口
	 * @param username
	 *            ftp用户名
	 * @param password
	 *            ftp密码
	 * @return 一个客户端
	 */
	public static FTPClient makeFtpConnection(String host, int port, String username, String password)
	{
		FTPClient client = new FTPClient();
		client.setType(FTPClient.TYPE_BINARY);
		try {
			client.connect(host, port);
			client.login(username, password);
		} catch (Exception e) {
			throw new FTPRuntimeException(e);
		}
		return client;
	}

	/**
	 * 创建文件夹
	 * 
	 * @param client
	 *            FTP客户端
	 * @param remoteDirectory
	 *            FTP文件夹路径
	 * @throws IllegalStateException
	 * @throws IOException
	 * @throws FTPIllegalReplyException
	 * @throws FTPException
	 */
	public static void makeDirectory(FTPClient client, String remoteDirectory) throws IllegalStateException, IOException, FTPIllegalReplyException, FTPException
	{
		String[] rds = FileUtil.formatFileSeparator(remoteDirectory).split("\\/");
		int isExist = -1;

		for (int i = 0; i < rds.length; i++) {
			if (StringUtils.isEmpty(rds[i])) {
				continue;
			}

			isExist = isExist(client, rds[i]);

			if (isExist == -1) {
				client.createDirectory(rds[i]);
			} else if (isExist == 1) {
				LOGGER.info("directory : " + rds[i] + " Exist!.");
			} else {
				LOGGER.error("Make directory : " + rds[i] + " Fail.");
				return;
			}

			// 加入rds[i]是空文件夹，会有550的failed to change directory的异常
			client.changeDirectory(rds[i]);
		}
	}

	/**
	 * FTP下载文件到本地一个文件夹,如果本地文件夹不存在，则创建必要的目录结构
	 * 
	 * @param client
	 *            FTP客户端
	 * @param remoteFileName
	 *            FTP文件
	 * @param localFolderPath
	 *            存的本地目录
	 * 
	 * @throws FTPRuntimeException
	 */
	public static void download(FTPClient client, String remoteFileName, String localFolderPath) throws FTPRuntimeException
	{
		int x = isExist(client, remoteFileName);
		MyFtpListener listener = MyFtpListener.instance(FTPOptType.DOWN);
		File localFolder = new File(localFolderPath);
		if (localFolder.isFile()) {
			throw new FTPRuntimeException("所要的下载保存的地方是一个文件，无法保存！");
		} else {
			if (!localFolder.exists())
				localFolder.mkdirs();
		}
		if (x == FTPFile.TYPE_FILE) {
			String localfilepath = PathToolkit.formatPath4File(localFolderPath + File.separator + new File(remoteFileName).getName());
			try {
				if (listener != null)
					client.download(remoteFileName, new File(localfilepath), listener);
				else
					client.download(remoteFileName, new File(localfilepath));
			} catch (Exception e) {
				throw new FTPRuntimeException(e);
			}
		} else {
			throw new FTPRuntimeException("所要下载的文件 " + remoteFileName + " 不存在！");
		}
	}

	/**
	 * 到配置文件中默认的FTP服务器上下载文件
	 * 
	 * @param remoteFilePath
	 *            FTP文件路径
	 * @param localFolderPath
	 *            存的本地目录
	 * @throws FTPRuntimeException
	 */
	public static void downloadDefault(String remoteFilePath, String localFolderPath) throws FTPRuntimeException
	{
		FTPClient client = FTPToolkit.makeFtpConnection(Configuration.get(Constants.FTP_IP), Configuration.getInt(Constants.FTP_PORT), Configuration.get(Constants.FTP_USERNAME), Configuration.get(Constants.FTP_PASSWORD));
		download(client, remoteFilePath, localFolderPath);
		FTPToolkit.closeConnection(client);
	}

	/**
	 * 上传本地文件localFilePath到配置文件中默认的FTP服务器指定目录remoteFolderPath下，命名为remoteFileName
	 * 
	 * @param localFilePath
	 *            本地文件
	 * @param remoteFolderPath
	 *            FTP文件夹
	 * @param remoteFileName
	 *            FTP文件
	 * @return 
	 *         上传成功返回非负数(0便是上传文件已经存在，1表示不存在情况下上传成功)，失败返回负数(-1本地上传文件不存在，-2上传中发生异常)
	 * @throws FTPRuntimeException
	 */
	public static int uploadDefault(String localFilePath, String remoteFolderPath, String remoteFileName) throws FTPRuntimeException
	{
		File localFile = new File(localFilePath);
		String remoteFilePath = remoteFolderPath + "/" + remoteFileName;

		if (!localFile.exists()) {
			LOGGER.error("local file " + localFilePath + "doesn't exsit.");
			return -1;
		}

		FTPClient client = FTPToolkit.makeFtpConnection(Configuration.get(Constants.FTP_IP), Configuration.getInt(Constants.FTP_PORT), Configuration.get(Constants.FTP_USERNAME), Configuration.get(Constants.FTP_PASSWORD));

		if (isExist(client, remoteFilePath) == 0) {
			LOGGER.info("remote file " + remoteFilePath + " exsits.");
			return 0;
		}

		try {
			if (isExist(client, remoteFolderPath) <= 0) {
				FTPToolkit.makeDirectory(client, remoteFolderPath);
			}
			FTPToolkit.upload(client, localFilePath, remoteFolderPath);
			client.changeDirectory(remoteFolderPath);
			client.rename(localFile.getName(), remoteFileName);
		} catch (IllegalStateException e) {
			LOGGER.error("wrong ftp state error.", e);
			return -2;
		} catch (IOException e) {
			LOGGER.error("IO error.", e);
			return -2;
		} catch (FTPIllegalReplyException e) {
			LOGGER.error("wrong ftp reply error.", e);
			return -2;
		} catch (FTPException e) {
			LOGGER.error("ftp error.", e);
			return -2;
		} finally {
			FTPToolkit.closeConnection(client);
		}
		return 1;
	}

	/**
	 * FTP上传本地文件到FTP的一个目录下
	 * 
	 * @param client
	 *            FTP客户端
	 * @param localfile
	 *            本地文件
	 * @param remoteFolderPath
	 *            FTP上传目录
	 */
	public static void upload(FTPClient client, File localfile, String remoteFolderPath)
	{
		remoteFolderPath = PathToolkit.formatPath4FTP(remoteFolderPath);
		MyFtpListener listener = MyFtpListener.instance(FTPOptType.UP);
		try {
			client.changeDirectory(remoteFolderPath);
			if (!localfile.exists())
				throw new FTPRuntimeException("所要上传的FTP文件" + localfile.getPath() + "不存在！");
			if (!localfile.isFile())
				throw new FTPRuntimeException("所要上传的FTP文件" + localfile.getPath() + "是一个文件夹！");
			if (listener != null)
				client.upload(localfile, listener);
			else
				client.upload(localfile);
			client.changeDirectory("/");
		} catch (Exception e) {
			throw new FTPRuntimeException(e);
		}
	}

	/**
	 * FTP上传本地文件到FTP的一个目录下
	 * 
	 * @param client
	 *            FTP客户端
	 * @param localfilepath
	 *            本地文件路径
	 * @param remoteFolderPath
	 *            FTP上传目录
	 */
	public static void upload(FTPClient client, String localfilepath, String remoteFolderPath)
	{
		File localfile = new File(localfilepath);
		upload(client, localfile, remoteFolderPath);
	}

	/**
	 * 批量上传本地文件到FTP指定目录上
	 * 
	 * @param client
	 *            FTP客户端
	 * @param localFilePaths
	 *            本地文件路径列表
	 * @param remoteFolderPath
	 *            FTP上传目录
	 */
	public static void uploadListPath(FTPClient client, List<String> localFilePaths, String remoteFolderPath)
	{
		remoteFolderPath = PathToolkit.formatPath4FTP(remoteFolderPath);
		try {
			client.changeDirectory(remoteFolderPath);
			MyFtpListener listener = MyFtpListener.instance(FTPOptType.UP);
			for (String path : localFilePaths) {
				File file = new File(path);
				if (!file.exists())
					throw new FTPRuntimeException("所要上传的FTP文件" + path + "不存在！");
				if (!file.isFile())
					throw new FTPRuntimeException("所要上传的FTP文件" + path + "是一个文件夹！");
				if (listener != null)
					client.upload(file, listener);
				else
					client.upload(file);
			}
			client.changeDirectory("/");
		} catch (Exception e) {
			throw new FTPRuntimeException(e);
		}
	}

	/**
	 * 批量上传本地文件到FTP指定目录上
	 * 
	 * @param client
	 *            FTP客户端
	 * @param localFiles
	 *            本地文件列表
	 * @param remoteFolderPath
	 *            FTP上传目录
	 */
	public static void uploadListFile(FTPClient client, List<File> localFiles, String remoteFolderPath)
	{
		try {
			client.changeDirectory(remoteFolderPath);
			remoteFolderPath = PathToolkit.formatPath4FTP(remoteFolderPath);
			MyFtpListener listener = MyFtpListener.instance(FTPOptType.UP);
			for (File file : localFiles) {
				if (!file.exists())
					throw new FTPRuntimeException("所要上传的FTP文件" + file.getPath() + "不存在！");
				if (!file.isFile())
					throw new FTPRuntimeException("所要上传的FTP文件" + file.getPath() + "是一个文件夹！");
				if (listener != null)
					client.upload(file, listener);
				else
					client.upload(file);
			}
			client.changeDirectory("/");
		} catch (Exception e) {
			throw new FTPRuntimeException(e);
		}
	}

	/**
	 * 判断一个FTP路径是否存在，如果存在返回类型(FTPFile.TYPE_DIRECTORY=1、FTPFile.TYPE_FILE=0、
	 * FTPFile.TYPE_LINK=2) 如果文件不存在，则返回一个-1
	 * 
	 * @param client
	 *            FTP客户端
	 * @param remotePath
	 *            FTP文件或文件夹路径
	 * @return 存在时候返回类型值(文件0，文件夹1，连接2)，不存在则返回-1
	 */
	public static int isExist(FTPClient client, String remotePath)
	{
		if (remotePath == null) {
			return -1;
		}
		remotePath = PathToolkit.formatPath4FTP(remotePath);
		FTPFile[] list = null;
		try {
			// TODO head over flow
			list = client.list(remotePath);
		} catch (Exception e) {
			return -1;
		}
		if (list.length > 1)
			return FTPFile.TYPE_DIRECTORY;
		else if (list.length == 1) {
			FTPFile f = list[0];
			if (f.getType() == FTPFile.TYPE_DIRECTORY)
				return FTPFile.TYPE_DIRECTORY;
			// 假设推理判断
			String _path = remotePath + "/" + f.getName();
			try {
				int y = client.list(_path).length;
				if (y == 1)
					return FTPFile.TYPE_DIRECTORY;
				else
					return FTPFile.TYPE_FILE;
			} catch (Exception e) {
				return FTPFile.TYPE_FILE;
			}
		} else {
			try {
				client.changeDirectory(remotePath);
				return FTPFile.TYPE_DIRECTORY;
			} catch (Exception e) {
				return -1;
			}
		}
	}

	/**
	 * 判断一个FTP文件夹路径是否存在并且不为空
	 * 
	 * @param client
	 *            FTP客户端
	 * @param remoteFolderPath
	 *            FTP文件夹路径
	 * @return 存在返回值(0表示文件夹为空，1表示不为空)，不存在返回-1
	 */
	public static int isDirectoryEmpty(FTPClient client, String remoteFolderPath)
	{
		if (isExist(client, remoteFolderPath) != 1) {
			return -1;
		}
		try {
			client.changeDirectory(remoteFolderPath);
			if (client.listNames().length > 0) {
				return 1;
			} else {
				return 0;
			}
		} catch (Exception e) {
			LOGGER.error("ftp list error.", e);
			return -1;
		}
	}

	/**
	 * 关闭FTP连接，关闭时候像服务器发送一条关闭命令
	 * 
	 * @param client
	 *            FTP客户端
	 * @return 关闭成功，或者链接已断开，或者链接为null时候返回true，通过两次关闭都失败时候返回false
	 */

	public static boolean closeConnection(FTPClient client)
	{
		if (client == null)
			return true;
		if (client.isConnected()) {
			try {
				client.disconnect(true);
				return true;
			} catch (Exception e) {
				try {
					client.disconnect(false);
				} catch (Exception e1) {
					e1.printStackTrace();
					return false;
				}
			}
		}
		return true;
	}
}
